﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.Commands;
using Microsoft.Practices.ObjectBuilder;
using WPFCABShell.Shell.Interface;
using WPFCABShell.Shell.Interface.Services.Command;
using WPFCABShell.Shell.Interface.Services.Menu;
using WPFCABShell.Shell.Interface.Services.Options;
using WPFCABShell.Shell.Interface.Services.Skinning;
using WPFCABShell.Shell.Interface.Services.StatusBar;
using WPFCABShell.Shell.Interface.Services.ToolBar;
using WPFCABShell.Shell.Options;
using WPFCABShell.Shell.Options.Plugins;
using Microsoft.Practices.CompositeUI.SmartParts;
using SCSFContrib.CompositeUI.WPF.Workspaces;
using CAB = Microsoft.Practices.CompositeUI.Commands;
using WPF = System.Windows.Input;

namespace WPFCABShell.Shell
{
	/// <summary>
	/// Implements the root work item for the CAB application.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The root work item is a container for all components and child work items in the CAB application. It
	/// is started during the initialization of the application.
	/// </para>
	/// </remarks>
	internal sealed class RootWorkItem : WorkItem
	{
		private ICommandService _commandService;
		private IMenuService _menuService;
		private IOptionsService _optionsService;
		private ISkinningService _skinningService;
		private IStatusBarService _statusBarService;
		private IToolBarService _toolBarService;
		private readonly IDictionary<string, IMenuItem> _mainMenuViewToolBarItems;
		private readonly IDictionary<string, IMenuItem> _toolBarsContextMenuToolBarItems;
		private readonly IDictionary<Command, string> _commandToToolBars;
		private readonly IDictionary<Command, ISkin> _commandToSkin;
		private readonly IDictionary<ISkin, IMenuItem> _skinsToMenuItems;
		private const string _defaultSkinName = "Default";
		private const string _mainWindowViewId = "view://MainWindow";
		private const string _optionsViewId = "view://Options";

		[Dependency]
		public ICommandService CommandService
		{
			set
			{
				_commandService = value;
			}
		}

		[Dependency]
		public IMenuService MenuService
		{
			set
			{
				_menuService = value;
			}
		}

		[Dependency]
		public IOptionsService OptionsService
		{
			set
			{
				_optionsService = value;
			}
		}

		[Dependency]
		public ISkinningService SkinningService
		{
			set
			{
				_skinningService = value;
			}
		}

		[Dependency]
		public IStatusBarService StatusBarService
		{
			set
			{
				_statusBarService = value;
			}
		}

		[Dependency]
		public IToolBarService ToolBarService
		{
			set
			{
				_toolBarService = value;
			}
		}

		public RootWorkItem()
		{
			_mainMenuViewToolBarItems = new Dictionary<string, IMenuItem>();
			_toolBarsContextMenuToolBarItems = new Dictionary<string, IMenuItem>();
			_commandToToolBars = new Dictionary<Command, string>();
			_commandToSkin = new Dictionary<Command, ISkin>();
			_skinsToMenuItems = new Dictionary<ISkin, IMenuItem>();
		}

		protected override void OnInitialized()
		{
			base.OnInitialized();

			//add in workspaces required for the whole application
			Workspaces.AddNew<WindowWorkspace>(Shell.Interface.Workspaces.WindowWorkspace);
		}

		public override void OnBuiltUp(string id)
		{
			base.OnBuiltUp(id);

			//create the main window
			MainWindow mainWindow = Items.AddNew<MainWindow>(_mainWindowViewId);

			//register parts of the main window with CAB
			UIExtensionSites.RegisterSite(ExtensionSites.Menu.Name, mainWindow.MainMenu);
			UIExtensionSites.RegisterSite(ExtensionSites.ToolBar.Name, mainWindow.ToolBarTray);
			UIExtensionSites.RegisterSite(ExtensionSites.ContextMenus.ToolBars.Name, mainWindow.ToolBarsContextMenu);
			Workspaces.Add(mainWindow.MainWorkspace, Interface.Workspaces.MainWorkspace);

			//create and register shared commands
			ICommand undoCommand = _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Undo, WPF.ApplicationCommands.Undo.Name, CommandCategories.Edit, null, null);
			ICommand redoCommand = _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Redo, WPF.ApplicationCommands.Redo.Name, CommandCategories.Edit, null, null);
			ICommand cutCommand = _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Cut, WPF.ApplicationCommands.Cut.Name, CommandCategories.Edit, null, null);
			ICommand copyCommand = _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Copy, WPF.ApplicationCommands.Copy.Name, CommandCategories.Edit, null, null);
			ICommand pasteCommand = _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Paste, WPF.ApplicationCommands.Paste.Name, CommandCategories.Edit, null, null);

			//use services to register items under the roots
			_menuService.Add(ExtensionSites.Menu.Name, _commandService.CreateCommand("_File", null), ExtensionSites.Menu.File.GroupIndex, ExtensionSites.Menu.File.Index, ExtensionSites.Menu.File.Name);
			_menuService.Add(ExtensionSites.Menu.Name, _commandService.CreateCommand("_Edit", null), ExtensionSites.Menu.Edit.GroupIndex, ExtensionSites.Menu.Edit.Index, ExtensionSites.Menu.Edit.Name);
			_menuService.Add(ExtensionSites.Menu.Name, _commandService.CreateCommand("_View", null), ExtensionSites.Menu.View.GroupIndex, ExtensionSites.Menu.View.Index, ExtensionSites.Menu.View.Name);
			_menuService.Add(ExtensionSites.Menu.Name, _commandService.CreateCommand("_Tools", null), ExtensionSites.Menu.Tools.GroupIndex, ExtensionSites.Menu.Tools.Index, ExtensionSites.Menu.Tools.Name);
			_menuService.Add(ExtensionSites.Menu.Name, _commandService.CreateCommand("_Help", null), ExtensionSites.Menu.Help.GroupIndex, ExtensionSites.Menu.Help.Index, ExtensionSites.Menu.Help.Name);

			_menuService.Add(ExtensionSites.Menu.File.Name, _commandService.CreateAndRegisterCommand(Commands[Interface.Commands.Exit], Interface.Commands.Exit, CommandCategories.File, "_Exit", null), ExtensionSites.Menu.File.Exit.GroupIndex, ExtensionSites.Menu.File.Exit.Index, null);

			_menuService.Add(ExtensionSites.Menu.Edit.Name, undoCommand, ExtensionSites.Menu.Edit.Undo.GroupIndex, ExtensionSites.Menu.Edit.Undo.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, redoCommand, ExtensionSites.Menu.Edit.Redo.GroupIndex, ExtensionSites.Menu.Edit.Redo.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, cutCommand, ExtensionSites.Menu.Edit.Cut.GroupIndex, ExtensionSites.Menu.Edit.Cut.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, copyCommand, ExtensionSites.Menu.Edit.Copy.GroupIndex, ExtensionSites.Menu.Edit.Copy.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, pasteCommand, ExtensionSites.Menu.Edit.Paste.GroupIndex, ExtensionSites.Menu.Edit.Paste.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.Delete, WPF.ApplicationCommands.Delete.Name, CommandCategories.Edit, null, null), ExtensionSites.Menu.Edit.Delete.GroupIndex, ExtensionSites.Menu.Edit.Delete.Index, null);
			_menuService.Add(ExtensionSites.Menu.Edit.Name, _commandService.CreateAndRegisterCommand(WPF.ApplicationCommands.SelectAll, WPF.ApplicationCommands.SelectAll.Name, CommandCategories.Edit, null, null), ExtensionSites.Menu.Edit.SelectAll.GroupIndex, ExtensionSites.Menu.Edit.SelectAll.Index, null);

			_menuService.Add(ExtensionSites.Menu.View.Name, _commandService.CreateCommand("_Toolbars", null), ExtensionSites.Menu.View.ToolBars.GroupIndex, ExtensionSites.Menu.View.ToolBars.Index, ExtensionSites.Menu.View.ToolBars.Name);
			_menuService.Add(ExtensionSites.Menu.View.Name, _commandService.CreateCommand("_Skins", null), ExtensionSites.Menu.View.Skins.GroupIndex, ExtensionSites.Menu.View.Skins.Index, ExtensionSites.Menu.View.Skins.Name);

			_menuService.Add(ExtensionSites.Menu.Tools.Name, _commandService.CreateAndRegisterCommand(Commands[Interface.Commands.Options], Interface.Commands.Options, CommandCategories.Tools, "_Options...", null), ExtensionSites.Menu.Tools.Options.GroupIndex, ExtensionSites.Menu.Tools.Options.Index, null);

			//handle various tool bar events to keep menu items in sync
			_toolBarService.ToolBarAdded += _toolBarService_ToolBarAdded;
			_toolBarService.ToolBarRemoved += _toolBarService_ToolBarRemoved;
			_toolBarService.ToolBarVisibilityChanged += _toolBarService_ToolBarVisibilityChanged;

			_toolBarService.AddButtonItem(ExtensionSites.ToolBar.Edit.Name, undoCommand, ExtensionSites.ToolBar.Edit.Undo.GroupIndex, ExtensionSites.ToolBar.Edit.Undo.Index);
			_toolBarService.AddButtonItem(ExtensionSites.ToolBar.Edit.Name, redoCommand, ExtensionSites.ToolBar.Edit.Redo.GroupIndex, ExtensionSites.ToolBar.Edit.Redo.Index);
			_toolBarService.AddButtonItem(ExtensionSites.ToolBar.Edit.Name, cutCommand, ExtensionSites.ToolBar.Edit.Cut.GroupIndex, ExtensionSites.ToolBar.Edit.Cut.Index);
			_toolBarService.AddButtonItem(ExtensionSites.ToolBar.Edit.Name, copyCommand, ExtensionSites.ToolBar.Edit.Copy.GroupIndex, ExtensionSites.ToolBar.Edit.Copy.Index);
			_toolBarService.AddButtonItem(ExtensionSites.ToolBar.Edit.Name, pasteCommand, ExtensionSites.ToolBar.Edit.Paste.GroupIndex, ExtensionSites.ToolBar.Edit.Paste.Index);

			//set root status bar messages
			_statusBarService.BeginMessageOperation(StatusPriority.Low).Text = "Ready";

			//handle various skin events to keep menu items and current skin in sync
			_skinningService.SkinAdded += _skinningService_SkinAdded;
			_skinningService.SkinChanged += _skinningService_SkinChanged;

			//create the options view and register the options extensibility point
			IOptionsView optionsView = Items.AddNew<OptionsView>(_optionsViewId);
			UIExtensionSites.RegisterSite(ExtensionSites.Options.Name, optionsView.OptionsSite);

			//add plugins to the options pane
			_optionsService.Register(ExtensionSites.Options.Name, "Plugins", typeof(PluginsView), null);

			//register and assign the default skin
			_skinningService.CurrentSkin = _skinningService.Add("Default", typeof(DefaultSkin));

			//skin is loaded so we can try to resolve images
			undoCommand.Image = Application.Current.FindResource("Undo") as ImageSource;
			redoCommand.Image = Application.Current.FindResource("Redo") as ImageSource;
			cutCommand.Image = Application.Current.FindResource("Cut") as ImageSource;
			copyCommand.Image = Application.Current.FindResource("Copy") as ImageSource;
			pasteCommand.Image = Application.Current.FindResource("Paste") as ImageSource;
		}

		protected override void OnRunStarted()
		{
			base.OnRunStarted();

			MainWindow mainWindow = Items.Get<MainWindow>(_mainWindowViewId);

			//tell WPF the shutdown behavior for our application
			Application.Current.MainWindow = mainWindow;
			Application.Current.ShutdownMode = ShutdownMode.OnMainWindowClose;

			//show the main window
			mainWindow.Show();
		}

		[CommandHandler(Interface.Commands.Options)]
		public void Options(object sender, EventArgs e)
		{
			IOptionsView optionsView = Items.Get<IOptionsView>(_optionsViewId);
			WindowSmartPartInfo windowSmartPartInfo = new WindowSmartPartInfo("Options", Items.Get(_mainWindowViewId) as Window);
			windowSmartPartInfo.IsModal = true;
			windowSmartPartInfo.ResizeMode = ResizeMode.CanResize;
			windowSmartPartInfo.ShowInTaskbar = false;
			windowSmartPartInfo.StartupLocation = WindowStartupLocation.CenterOwner;
			IWorkspace windowWorkspace = Workspaces.Get<IWorkspace>(Interface.Workspaces.WindowWorkspace);
			windowWorkspace.Show(optionsView, windowSmartPartInfo);

			//give the view a chance to clean up
			optionsView.Closed(windowSmartPartInfo.DialogResult.Value);
		}

		private void _toolBarService_ToolBarAdded(object sender, ToolBarEventArgs e)
		{
			Command command = Commands[string.Concat("command://ToggleToolBar/", e.ToolBarSiteName)];
			command.ExecuteAction += ToggleToolBarVisibility_ExecuteAction;

			IMenuItem mainMenuItem = _menuService.Add(ExtensionSites.Menu.View.ToolBars.Name, _commandService.CreateCommand(command, GetToolBarName(e.ToolBarSiteName), null), null, null, null);
			IMenuItem contextMenuItem = _menuService.Add(ExtensionSites.ContextMenus.ToolBars.Name, _commandService.CreateCommand(command, GetToolBarName(e.ToolBarSiteName), null), null, null, null);

			mainMenuItem.IsChecked = _toolBarService.GetIsVisible(e.ToolBarSiteName);
			contextMenuItem.IsChecked = _toolBarService.GetIsVisible(e.ToolBarSiteName);

			_commandToToolBars[command] = e.ToolBarSiteName;
			_mainMenuViewToolBarItems[e.ToolBarSiteName] = mainMenuItem;
			_toolBarsContextMenuToolBarItems[e.ToolBarSiteName] = contextMenuItem;
		}

		private void _toolBarService_ToolBarRemoved(object sender, ToolBarEventArgs e)
		{
			IMenuItem mainMenuItem = _mainMenuViewToolBarItems[e.ToolBarSiteName];
			IMenuItem contextMenuItem = _toolBarsContextMenuToolBarItems[e.ToolBarSiteName];

			_menuService.Remove(mainMenuItem);
			_menuService.Remove(contextMenuItem);

			Command command = Commands[string.Concat("command://ToggleToolBar/", e.ToolBarSiteName)];
			_commandToToolBars.Remove(command);

			_mainMenuViewToolBarItems.Remove(e.ToolBarSiteName);
			_toolBarsContextMenuToolBarItems.Remove(e.ToolBarSiteName);
		}

		private void _toolBarService_ToolBarVisibilityChanged(object sender, ToolBarEventArgs e)
		{
			IMenuItem mainMenuItem = _mainMenuViewToolBarItems[e.ToolBarSiteName];
			IMenuItem contextMenuItem = _toolBarsContextMenuToolBarItems[e.ToolBarSiteName];
			mainMenuItem.IsChecked = _toolBarService.GetIsVisible(e.ToolBarSiteName);
			contextMenuItem.IsChecked = _toolBarService.GetIsVisible(e.ToolBarSiteName);
		}

		private void ToggleToolBarVisibility_ExecuteAction(object sender, EventArgs e)
		{
			Debug.Assert(sender is Command);
			string toolBarSiteName = _commandToToolBars[sender as Command];
			//use the tool bar service to toggle visibility
			_toolBarService.SetIsVisible(toolBarSiteName, !_toolBarService.GetIsVisible(toolBarSiteName));
		}

		private string GetToolBarName(string toolBarSiteName)
		{
			Debug.Assert(toolBarSiteName != null);
			return toolBarSiteName.Substring(toolBarSiteName.LastIndexOf("/") + 1);
		}

		private void _skinningService_SkinChanged(object sender, SkinChangedEventArgs e)
		{
			Collection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
			Debug.Assert(mergedDictionaries.Count == 1 || mergedDictionaries.Count == 2, "Expected either default dictionary, or default plus custom skin dictionary.");

			if (Application.Current.Resources.MergedDictionaries.Count == 2)
			{
				Application.Current.Resources.MergedDictionaries.RemoveAt(1);
			}

			if (e.NewSkin.ResourcesType != typeof(DefaultSkin))
			{
				mergedDictionaries.Add(Activator.CreateInstance(e.NewSkin.ResourcesType) as ResourceDictionary);
			}

			if (e.OldSkin != null)
			{
				_skinsToMenuItems[e.OldSkin].IsChecked = false;
			}

			_skinsToMenuItems[e.NewSkin].IsChecked = true;
		}

		private void _skinningService_SkinAdded(object sender, SkinEventArgs e)
		{
			Command command = Commands[string.Concat("command://SelectSkin/", e.Skin.Name)];
			command.ExecuteAction += SelectSkin_ExecuteAction;

			int groupIndex = string.Equals(_defaultSkinName, e.Skin.Name, StringComparison.Ordinal) ? 0 : 1;
			IMenuItem mainMenuItem = _menuService.Add(ExtensionSites.Menu.View.Skins.Name, _commandService.CreateCommand(command, e.Skin.Name, null), groupIndex, null, null);
			mainMenuItem.IsChecked = object.ReferenceEquals(e.Skin, _skinningService.CurrentSkin);

			_commandToSkin[command] = e.Skin;
			_skinsToMenuItems[e.Skin] = mainMenuItem;
		}

		private void SelectSkin_ExecuteAction(object sender, EventArgs e)
		{
			Debug.Assert(sender is Command);
			Debug.Assert(_commandToSkin.ContainsKey(sender as Command));
			//use the skinning service to change the skin
			_skinningService.CurrentSkin = _commandToSkin[sender as Command];
		}
	}
}